import 'package:winkai/db/dao/msg_dao.dart';
import 'package:winkai/db/dao/ses_dao.dart';
import 'package:winkai/db/database.dart';
import 'package:winkai/db/db_ext.dart';
import 'package:winkai/service/api_service.dart';
import 'package:winkai/entity/mse_rpy_rsp.dart';
import 'package:winkai/ext/ext.dart';
import 'package:drift/drift.dart';
import 'package:winkai/helper/chat_helper.dart';
import 'package:winkai/helper/msg_helper.dart';
import 'package:winkai/helper/msg_receive_helper.dart';
import 'package:winkai/helper/ses_helper.dart';
import 'package:winkai/helper/user_helper.dart';
import 'package:winkai/main.dart';

import '../../entity/msg_constant.dart';
import '../msg_service.dart';
import '../ses_service.dart';

class MsgServiceImp implements MsgService {
  @override
  Future<List<MsgInfo>> getMsgList(String sessionId,
      {bool isAsc = true, int? limit, int? offset}) {
    return rootDb.msgDao.selectBySessionId(sessionId,
        isAsc: isAsc, limit: limit, offset: offset);
  }

  @override
  Future<MsgInfo?> insertOrUpdateMsg(
    String content, {
    String? sessionId,
    String? userId,
    int? msgId,
    String? sendFailReason,
    String? serviceMsgId,
    List<String>? serviceNextMessageIds,
    MsgStatus status = MsgStatus.sended,
    MsgContentType contentType = MsgContentType.text,
    bool isPrivateChat = false,
    bool isCreate = true,
  }) async {
    if (sessionId == null) {
      return null;
    }
    msgId ??= genMsgId();
    var accountId = UserHelper().userId;
    await rootDb.msgDao.insertOrUpdate(MsgTbCompanion.insert(
      accountId: accountId,
      content: Value(content),
      contentType: contentType,
      sessionId: sessionId,
      isRead: const Value(true),
      userId: userId ?? UserHelper().userId.toString(),
      sendFailReason: sendFailReason.toDrifitNullSafeValue,
      serviceMsgId: serviceMsgId.toDrifitNullSafeValue,
      serviceNextMsgIds:
          serviceNextMessageIds.toJsonString.toDrifitNullSafeValue,
      status: status,
      isPrivateChat: isPrivateChat.toDrifitValue,
      id: msgId,
    ));
    await rootDb.sesDao.updateLastMsgId(sessionId, msgId);
    final msg = await rootDb.msgDao.selectByIdSingleOrNull(msgId);
    if (msg != null) {
      if (isCreate) {
        MsgHelper().add(msg);
        var session = await rootDb.sesDao.selectSingleOrNull(sessionId);
        var userId = getIt<SesService>().getUserIdBySessionId(sessionId);
        if (session == null) {
          await rootDb.sesDao.insertOrUpdate(
            accountId: accountId,
            id: sessionId,
            lastMsgId: msgId,
            userId: userId ?? UserHelper().userId.toString(),
            unReadNum:
                msg.msg.contentType == MsgContentType.waitAnswer ? 0 : 1,
            updateTime: DateTime.now(),
          );
          session = await rootDb.sesDao.selectSingleOrNull(sessionId);
          if (msg.msg.contentType != MsgContentType.waitAnswer &&
              userId != UserHelper().userId.toString()) {
            UserHelper().add(1);
          }
        } else {
          await _updateUnReadNumAndLastMsgId(session);
        }
        if (session != null) {
          session.message = msg.msg;
          SesHelper().update(session);
        }
      } else {
        MsgHelper().update(msg);
      }
    }
    return msg;
  }

  /// 更新会话消息未读数量
  Future<void> _updateUnReadNumAndLastMsgId(SesInfo sessionInfo) async {
    if (SesHelper().stack.peek()?.session.id != sessionInfo.session.id) {
      var lastMsgId = sessionInfo.message?.id ?? 0;
      var unReadNum = (sessionInfo.session.unReadNum) +
          (sessionInfo.message?.contentType == MsgContentType.waitAnswer
              ? 0
              : 1);
      sessionInfo.session = sessionInfo.session.copyWith(
        unReadNum: unReadNum,
        lastMsgId: lastMsgId.toDrifitNullSafeValue,
        updateTime: DateTime.now(),
      );
      sessionInfo.unreadCount = unReadNum;
      if (sessionInfo.message?.contentType != MsgContentType.waitAnswer &&
          sessionInfo.message?.userId != UserHelper().userId.toString()) {
        UserHelper().add(1);
      }
      await rootDb.sesDao.updateUnReadUnmAndLastMsgId(
          sessionInfo.session.id, unReadNum, lastMsgId);
    } else {
      var lastMsgId = sessionInfo.message?.id ?? 0;
      sessionInfo.session = sessionInfo.session.copyWith(
        lastMsgId: lastMsgId.toDrifitNullSafeValue,
        updateTime: DateTime.now(),
      );
      await rootDb.sesDao.updateLastMsgId(sessionInfo.session.id, lastMsgId);
    }
  }

  @override
  void sendMsg(
    String content, {
    String? sessionId,
    String? lastServiceMessageId,
    int? messageId,
    bool save = true,
  }) async {
    if (sessionId == null) return;
    int msgId = messageId ?? genMsgId();

    await getIt<SesService>().createSession(sessionId, lastMsgId: msgId);
    if (save) {
      await insertOrUpdateMsg(
        content,
        sessionId: sessionId,
        msgId: msgId,
        status: MsgStatus.sending,
        contentType: MsgContentType.text,
        isCreate: true,
      );
    }

    var userName = getIt<SesService>().getUserNameBySessionId(sessionId);
    var user = UserHelper().user;
    String userId = user.id;

    bool isRegister = UserHelper().isRegisterAccount;

    ///未注册时躺尸注册账号
    if (!isRegister) {
      isRegister = await UserHelper().tryRegisterAccount();
    }
    if (isRegister) {
      var result = await ApiService().sendMsg(
        userId,
        user.nickname ?? '',
        userName ?? '',
        content,
        msgId: lastServiceMessageId,
      );

      var data = result.data;

      await insertOrUpdateMsg(
        content,
        sessionId: sessionId,
        msgId: msgId,
        serviceMsgId: data?.msgId,
        serviceNextMessageIds: data?.nextMsgs,
        status: result.isSucceed ? MsgStatus.sended : MsgStatus.failure,
        sendFailReason: result.message,
        contentType: MsgContentType.text,
        isCreate: false,
      );

      ///开始检测发送消息结果
      if (result.isSucceed && data?.nextMsgs?.isNotEmpty == true) {
        ChatHelper().addSendFreeMsgCount();

        ///插入等待回到的消息
        var waitAnswerMsg = await addWaitAnswerMsg(
          sessionId: sessionId,
          serviceMessageId: data?.msgId,
          serviceNextMessageIds: data?.nextMsgs,
        );
        MsgReceiveHelper().startReceiveMsg(
          waitAnswerMsg,
          sessionId,
          data!.msgId!,
          data.nextMsgs!,
        );
      }
    } else {
      await insertOrUpdateMsg(
        content,
        sessionId: sessionId,
        msgId: msgId,
        status: MsgStatus.failure,
        sendFailReason: "Account registration failed",
        contentType: MsgContentType.text,
        isCreate: false,
      );
    }
  }

  @override
  Future<MsgInfo?> addWaitAnswerMsg({
    required String sessionId,
    String? serviceMessageId,
    List<String>? serviceNextMessageIds,
  }) async {
    var userId = getIt<SesService>().getUserIdBySessionId(sessionId);
    var msg = await insertOrUpdateMsg(
      '',
      userId: userId,
      sessionId: sessionId,
      serviceMsgId: serviceMessageId,
      serviceNextMessageIds: serviceNextMessageIds,
      status: MsgStatus.sended,
      contentType: MsgContentType.waitAnswer,
    );
    return msg;
  }

  @override
  Future<void> deleteMsg(MsgInfo messageInfo) async {
    await rootDb.msgDao.deleteById(messageInfo.msg.id);
    MsgHelper().remove(messageInfo);
  }

  @override
  Future<void> receiveMsgAnswer({
    required String sessionId,
    required String serviceMessageId,
    required MsgRpyRspAnswer answer,
  }) async {
    var userId = getIt<SesService>().getUserIdBySessionId(sessionId);
    MsgContentType contentType = MsgContentType.text;
    var isPrivateChat = answer.lockLevel == 'PRIVATE';
    if (answer.source == 'MEDIA') {
      contentType = MsgContentType.image;
    }
    await insertOrUpdateMsg(
      answer.content ?? '',
      userId: userId,
      sessionId: sessionId,
      serviceMsgId: serviceMessageId,
      status: MsgStatus.sended,
      contentType: contentType,
      isPrivateChat: isPrivateChat,
    );
  }

  @override
  Future<void> deleteBySessionId(String sessionId) async {
    await rootDb.msgDao.deleteBySessionId(sessionId);
  }
}
